<html>
<head><meta charset="utf-8"><title>Question about async Read/Write · wg-async-foundations · Zulip Chat Archive</title></head>
<h2>Stream: <a href="https://rust-lang.github.io/zulip_archive/stream/187312-wg-async-foundations/index.html">wg-async-foundations</a></h2>
<h3>Topic: <a href="https://rust-lang.github.io/zulip_archive/stream/187312-wg-async-foundations/topic/Question.20about.20async.20Read.2FWrite.html">Question about async Read/Write</a></h3>

<hr>

<base href="https://rust-lang.zulipchat.com">

<head><link href="https://rust-lang.github.io/zulip_archive/style.css" rel="stylesheet"></head>

<a name="247279504"></a>
<h4><a href="https://rust-lang.zulipchat.com#narrow/stream/187312-wg-async-foundations/topic/Question%20about%20async%20Read/Write/near/247279504" class="zl"><img src="https://rust-lang.github.io/zulip_archive/assets/img/zulip.svg" alt="view this post on Zulip" style="width:20px;height:20px;"></a> Nick Cameron <a href="https://rust-lang.github.io/zulip_archive/stream/187312-wg-async-foundations/topic/Question.20about.20async.20Read.2FWrite.html#247279504">(Jul 27 2021 at 00:14)</a>:</h4>
<p>It seems a challenge with the async read/write traits is for objects which want to do both (SSL is the example given in the vision doc). Why is this more of a challenge in the async world than in the sync world? In other words, why is option D from the doc (implement AsyncRead + AsyncWrite) not the obvious winning choice of API?</p>



<a name="247282653"></a>
<h4><a href="https://rust-lang.zulipchat.com#narrow/stream/187312-wg-async-foundations/topic/Question%20about%20async%20Read/Write/near/247282653" class="zl"><img src="https://rust-lang.github.io/zulip_archive/assets/img/zulip.svg" alt="view this post on Zulip" style="width:20px;height:20px;"></a> Matthias247 <a href="https://rust-lang.github.io/zulip_archive/stream/187312-wg-async-foundations/topic/Question.20about.20async.20Read.2FWrite.html#247282653">(Jul 27 2021 at 01:13)</a>:</h4>
<p>It is a challenge in the synchronous world, but also a matter of use-cases. Look at your favorite TLS library. You likely can either just do a blocking read, or a blocking write on it, but it's not full duplex. </p>
<p>That might not matter for a lot of use-cases, since e.g. for a trivial HTTP/1.1 client you don't need to read and write at the same time.<br>
For HTTP/2 library you need due to multiplexing. But everything which does that will also uses the event-driven versions of the TLS library.</p>
<p>I think Go and Java TLS libraries might be an exception here. In those cases the objects are just thread-safe reference types, and thereby equivalent to some kind of <code>Arc&lt;dyn Read + Write&gt;</code> types in the Rust world - but where the types only require <code>&amp;self</code>.</p>



<a name="247284061"></a>
<h4><a href="https://rust-lang.zulipchat.com#narrow/stream/187312-wg-async-foundations/topic/Question%20about%20async%20Read/Write/near/247284061" class="zl"><img src="https://rust-lang.github.io/zulip_archive/assets/img/zulip.svg" alt="view this post on Zulip" style="width:20px;height:20px;"></a> Nick Cameron <a href="https://rust-lang.github.io/zulip_archive/stream/187312-wg-async-foundations/topic/Question.20about.20async.20Read.2FWrite.html#247284061">(Jul 27 2021 at 01:38)</a>:</h4>
<p>So what exactly is the concrete problem with using just the Read and Write traits? E.g., if I have a TlsSocket type and it implements both Read and Write, or AsyncRead and AsyncWrite, traits, what is the code that I can't write?</p>



<a name="247302518"></a>
<h4><a href="https://rust-lang.zulipchat.com#narrow/stream/187312-wg-async-foundations/topic/Question%20about%20async%20Read/Write/near/247302518" class="zl"><img src="https://rust-lang.github.io/zulip_archive/assets/img/zulip.svg" alt="view this post on Zulip" style="width:20px;height:20px;"></a> Alice Ryhl <a href="https://rust-lang.github.io/zulip_archive/stream/187312-wg-async-foundations/topic/Question.20about.20async.20Read.2FWrite.html#247302518">(Jul 27 2021 at 07:34)</a>:</h4>
<p>Can you link the vision doc you're looking at?</p>



<a name="247304628"></a>
<h4><a href="https://rust-lang.zulipchat.com#narrow/stream/187312-wg-async-foundations/topic/Question%20about%20async%20Read/Write/near/247304628" class="zl"><img src="https://rust-lang.github.io/zulip_archive/assets/img/zulip.svg" alt="view this post on Zulip" style="width:20px;height:20px;"></a> Nick Cameron <a href="https://rust-lang.github.io/zulip_archive/stream/187312-wg-async-foundations/topic/Question.20about.20async.20Read.2FWrite.html#247304628">(Jul 27 2021 at 08:02)</a>:</h4>
<p><a href="https://nikomatsakis.github.io/wg-async-foundations/vision/deliverables/portable/async_read_write.html">https://nikomatsakis.github.io/wg-async-foundations/vision/deliverables/portable/async_read_write.html</a></p>



<a name="247304702"></a>
<h4><a href="https://rust-lang.zulipchat.com#narrow/stream/187312-wg-async-foundations/topic/Question%20about%20async%20Read/Write/near/247304702" class="zl"><img src="https://rust-lang.github.io/zulip_archive/assets/img/zulip.svg" alt="view this post on Zulip" style="width:20px;height:20px;"></a> Alice Ryhl <a href="https://rust-lang.github.io/zulip_archive/stream/187312-wg-async-foundations/topic/Question.20about.20async.20Read.2FWrite.html#247304702">(Jul 27 2021 at 08:03)</a>:</h4>
<p>You can't really implement <code>AsyncRead</code>/<code>AsyncWrite</code> for immutable references in a reasonable way</p>



<a name="247304797"></a>
<h4><a href="https://rust-lang.zulipchat.com#narrow/stream/187312-wg-async-foundations/topic/Question%20about%20async%20Read/Write/near/247304797" class="zl"><img src="https://rust-lang.github.io/zulip_archive/assets/img/zulip.svg" alt="view this post on Zulip" style="width:20px;height:20px;"></a> Alice Ryhl <a href="https://rust-lang.github.io/zulip_archive/stream/187312-wg-async-foundations/topic/Question.20about.20async.20Read.2FWrite.html#247304797">(Jul 27 2021 at 08:04)</a>:</h4>
<p>This is because poll methods generally come with a guarantee that only the most recent caller is woken, but this wont work if you have multiple readers via an immutable reference.</p>



<a name="247304840"></a>
<h4><a href="https://rust-lang.zulipchat.com#narrow/stream/187312-wg-async-foundations/topic/Question%20about%20async%20Read/Write/near/247304840" class="zl"><img src="https://rust-lang.github.io/zulip_archive/assets/img/zulip.svg" alt="view this post on Zulip" style="width:20px;height:20px;"></a> Alice Ryhl <a href="https://rust-lang.github.io/zulip_archive/stream/187312-wg-async-foundations/topic/Question.20about.20async.20Read.2FWrite.html#247304840">(Jul 27 2021 at 08:04)</a>:</h4>
<p>So you either have to risk not waking all readers, or store a possibly unbounded amount of wakers.</p>



<a name="247305453"></a>
<h4><a href="https://rust-lang.zulipchat.com#narrow/stream/187312-wg-async-foundations/topic/Question%20about%20async%20Read/Write/near/247305453" class="zl"><img src="https://rust-lang.github.io/zulip_archive/assets/img/zulip.svg" alt="view this post on Zulip" style="width:20px;height:20px;"></a> Kestrer <a href="https://rust-lang.github.io/zulip_archive/stream/187312-wg-async-foundations/topic/Question.20about.20async.20Read.2FWrite.html#247305453">(Jul 27 2021 at 08:12)</a>:</h4>
<p>(or do what async-std does, and have concurrent I/O operations be caught in a busy-loop of waking each other)</p>



<a name="247306106"></a>
<h4><a href="https://rust-lang.zulipchat.com#narrow/stream/187312-wg-async-foundations/topic/Question%20about%20async%20Read/Write/near/247306106" class="zl"><img src="https://rust-lang.github.io/zulip_archive/assets/img/zulip.svg" alt="view this post on Zulip" style="width:20px;height:20px;"></a> Kestrer <a href="https://rust-lang.github.io/zulip_archive/stream/187312-wg-async-foundations/topic/Question.20about.20async.20Read.2FWrite.html#247306106">(Jul 27 2021 at 08:21)</a>:</h4>
<p>If we get unsized function parameters, then AsyncWrite could be defined as an async trait that desugars to something like this:</p>
<div class="codehilite" data-code-language="Rust"><pre><span></span><code><span class="k">pub</span><span class="w"> </span><span class="k">trait</span><span class="w"> </span><span class="n">AsyncWrite</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="k">type</span> <span class="nc">Write</span><span class="o">&lt;'</span><span class="na">a</span><span class="o">&gt;</span>: <span class="nc">Future</span><span class="o">&lt;</span><span class="n">Output</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kt">usize</span><span class="o">&gt;</span><span class="p">;</span><span class="w"></span>
<span class="w">    </span><span class="k">fn</span> <span class="nf">write</span><span class="o">&lt;'</span><span class="na">a</span><span class="o">&gt;</span><span class="p">(</span><span class="o">&amp;'</span><span class="na">a</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="bp">self</span><span class="p">,</span><span class="w"> </span><span class="n">buf</span>: <span class="kp">&amp;</span><span class="o">'</span><span class="na">a</span> <span class="p">[</span><span class="kt">u8</span><span class="p">],</span><span class="w"> </span><span class="n">cb</span>: <span class="nc">dyn</span><span class="w"> </span><span class="nb">FnOnce</span><span class="p">(</span><span class="bp">Self</span>::<span class="n">Write</span><span class="o">&lt;'</span><span class="na">a</span><span class="o">&gt;</span><span class="p">));</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>
<p>When writing to a concrete type, the caller will simply allocate enough space for the future on the stack / in the containing future. Trait objects would implement it by setting <code>Write&lt;'a&gt; = dyn Future&lt;Output = usize&gt; + 'a</code>, so callers can choose how to deal with the unsized future: box it, <code>ReusableBoxFuture</code> it, etc. This trait would also work on shared references, because each I/O operation gets its own future.</p>



<a name="247368173"></a>
<h4><a href="https://rust-lang.zulipchat.com#narrow/stream/187312-wg-async-foundations/topic/Question%20about%20async%20Read/Write/near/247368173" class="zl"><img src="https://rust-lang.github.io/zulip_archive/assets/img/zulip.svg" alt="view this post on Zulip" style="width:20px;height:20px;"></a> Nick Cameron <a href="https://rust-lang.github.io/zulip_archive/stream/187312-wg-async-foundations/topic/Question.20about.20async.20Read.2FWrite.html#247368173">(Jul 27 2021 at 17:59)</a>:</h4>
<p>Sorry, I don't understand why being an immutable reference is important. It seems orthogonal whether a type is bounded by <code>AsyncReadWrite</code> or <code>AsyncRead + AsyncWrite</code> and whether the reference is <code>mut</code> or not</p>



<a name="247419701"></a>
<h4><a href="https://rust-lang.zulipchat.com#narrow/stream/187312-wg-async-foundations/topic/Question%20about%20async%20Read/Write/near/247419701" class="zl"><img src="https://rust-lang.github.io/zulip_archive/assets/img/zulip.svg" alt="view this post on Zulip" style="width:20px;height:20px;"></a> Andrew Chin (eminence) <a href="https://rust-lang.github.io/zulip_archive/stream/187312-wg-async-foundations/topic/Question.20about.20async.20Read.2FWrite.html#247419701">(Jul 28 2021 at 04:02)</a>:</h4>
<p>isn't "being an immutable reference" the defining property of Variant D (from the vision doc you linked)?</p>



<a name="247420388"></a>
<h4><a href="https://rust-lang.zulipchat.com#narrow/stream/187312-wg-async-foundations/topic/Question%20about%20async%20Read/Write/near/247420388" class="zl"><img src="https://rust-lang.github.io/zulip_archive/assets/img/zulip.svg" alt="view this post on Zulip" style="width:20px;height:20px;"></a> Nick Cameron <a href="https://rust-lang.github.io/zulip_archive/stream/187312-wg-async-foundations/topic/Question.20about.20async.20Read.2FWrite.html#247420388">(Jul 28 2021 at 04:19)</a>:</h4>
<p>Aiui, the defining property is that there are two independent traits?</p>



<a name="247420441"></a>
<h4><a href="https://rust-lang.zulipchat.com#narrow/stream/187312-wg-async-foundations/topic/Question%20about%20async%20Read/Write/near/247420441" class="zl"><img src="https://rust-lang.github.io/zulip_archive/assets/img/zulip.svg" alt="view this post on Zulip" style="width:20px;height:20px;"></a> Nick Cameron <a href="https://rust-lang.github.io/zulip_archive/stream/187312-wg-async-foundations/topic/Question.20about.20async.20Read.2FWrite.html#247420441">(Jul 28 2021 at 04:20)</a>:</h4>
<p>I guess if you have one object that need to be passed to two different functions at the same time, one for reading and one for writing, then they both have to be immutable</p>



<a name="247420461"></a>
<h4><a href="https://rust-lang.zulipchat.com#narrow/stream/187312-wg-async-foundations/topic/Question%20about%20async%20Read/Write/near/247420461" class="zl"><img src="https://rust-lang.github.io/zulip_archive/assets/img/zulip.svg" alt="view this post on Zulip" style="width:20px;height:20px;"></a> Nick Cameron <a href="https://rust-lang.github.io/zulip_archive/stream/187312-wg-async-foundations/topic/Question.20about.20async.20Read.2FWrite.html#247420461">(Jul 28 2021 at 04:21)</a>:</h4>
<p>Is that the scenario being targeted here? As opposed to say one function which reads and writes?</p>



<a name="247420775"></a>
<h4><a href="https://rust-lang.zulipchat.com#narrow/stream/187312-wg-async-foundations/topic/Question%20about%20async%20Read/Write/near/247420775" class="zl"><img src="https://rust-lang.github.io/zulip_archive/assets/img/zulip.svg" alt="view this post on Zulip" style="width:20px;height:20px;"></a> Andrew Chin (eminence) <a href="https://rust-lang.github.io/zulip_archive/stream/187312-wg-async-foundations/topic/Question.20about.20async.20Read.2FWrite.html#247420775">(Jul 28 2021 at 04:28)</a>:</h4>
<p>i think that's the scenario being targeted, yes (though i admit i'm not 100% clear)</p>



<a name="247423839"></a>
<h4><a href="https://rust-lang.zulipchat.com#narrow/stream/187312-wg-async-foundations/topic/Question%20about%20async%20Read/Write/near/247423839" class="zl"><img src="https://rust-lang.github.io/zulip_archive/assets/img/zulip.svg" alt="view this post on Zulip" style="width:20px;height:20px;"></a> Matthias247 <a href="https://rust-lang.github.io/zulip_archive/stream/187312-wg-async-foundations/topic/Question.20about.20async.20Read.2FWrite.html#247423839">(Jul 28 2021 at 05:36)</a>:</h4>
<p><span class="user-mention silent" data-user-id="218683">Alice Ryhl</span> <a href="#narrow/stream/187312-wg-async-foundations/topic/Question.20about.20async.20Read.2FWrite/near/247304797">said</a>:</p>
<blockquote>
<p>This is because poll methods generally come with a guarantee that only the most recent caller is woken, but this wont work if you have multiple readers via an immutable reference.</p>
</blockquote>
<p>Well, that's kind of a "epoll" wrapper problem, and less of one of the trailts in general. And you can even fix that. Eg.</p>
<div class="codehilite"><pre><span></span><code>struct ReadWriteImpl {
    inner: Mutex&lt;Inner&gt;;
    write_ready: AsyncManualResetEvent;
}

impl ReadWriteImpl&lt;T&gt; {
    async fn write(&amp;self, buf: &amp;[u8]) -&gt; Result&lt;usize, Error&gt; {
        loop {
            self.write_ready.wait().await;

            let inner = self.inner.lock();
            let written =  inner.write(buf)?;
            if written != 0 {
                return Ok(written);
            }
       }
    }

    /* Something equiivalent for read */
}
</code></pre></div>
<p>I guess this is also what <span class="user-mention" data-user-id="360486">@Kestrer</span> meant with "busy-loop".</p>



<a name="247423929"></a>
<h4><a href="https://rust-lang.zulipchat.com#narrow/stream/187312-wg-async-foundations/topic/Question%20about%20async%20Read/Write/near/247423929" class="zl"><img src="https://rust-lang.github.io/zulip_archive/assets/img/zulip.svg" alt="view this post on Zulip" style="width:20px;height:20px;"></a> Matthias247 <a href="https://rust-lang.github.io/zulip_archive/stream/187312-wg-async-foundations/topic/Question.20about.20async.20Read.2FWrite.html#247423929">(Jul 28 2021 at 05:39)</a>:</h4>
<p>Practically speaking I think while this is doable for low-level implementations, such an impl gets annoyinng for everyone who wants to do a wrapper. You want to have a writer which just counts the number of bytes written? Too bad, you either need an atomic, refcell or mutex.<br>
You want to do something even more complex, like compression? You almost certainly need mutex.</p>



<a name="247423989"></a>
<h4><a href="https://rust-lang.zulipchat.com#narrow/stream/187312-wg-async-foundations/topic/Question%20about%20async%20Read/Write/near/247423989" class="zl"><img src="https://rust-lang.github.io/zulip_archive/assets/img/zulip.svg" alt="view this post on Zulip" style="width:20px;height:20px;"></a> Kestrer <a href="https://rust-lang.github.io/zulip_archive/stream/187312-wg-async-foundations/topic/Question.20about.20async.20Read.2FWrite.html#247423989">(Jul 28 2021 at 05:41)</a>:</h4>
<p>That fix works because the <code>write</code> future is an async function, and not a <code>poll_*</code> function. If you tried to write that function as <code>async fn poll_write(&amp;self, ...)</code> (as the <code>AsyncWrite</code> trait requires) you would run into the issues Alice mentioned.</p>
<p>What I meant by "busy loop" is this:</p>
<ul>
<li>Task A tries to write to the <code>&amp;TcpStream</code>, sees it is not ready and registers its waker</li>
<li>Task B tries to write to the <code>&amp;TcpStream</code>, sees it is not ready and, upon seeing Task A's waker, wakes it and then replaces it with its own.</li>
<li>Task A is woken, sees the socket is not ready and upon seeing Task B's waker it wakes it and replaces it with its own.</li>
<li>Task B is woken, sees the socket is not ready and upon seeing Task A's waker it wakes it and replaces it with its own.</li>
<li>and so on and so on and so on...</li>
</ul>
<p>This is what happens in async-std when you try to write to a <code>&amp;TcpStream</code> concurrently.</p>



<a name="247424006"></a>
<h4><a href="https://rust-lang.zulipchat.com#narrow/stream/187312-wg-async-foundations/topic/Question%20about%20async%20Read/Write/near/247424006" class="zl"><img src="https://rust-lang.github.io/zulip_archive/assets/img/zulip.svg" alt="view this post on Zulip" style="width:20px;height:20px;"></a> Matthias247 <a href="https://rust-lang.github.io/zulip_archive/stream/187312-wg-async-foundations/topic/Question.20about.20async.20Read.2FWrite.html#247424006">(Jul 28 2021 at 05:41)</a>:</h4>
<p>And while I briefly thought "this could be interesting, because one could use the same trait to represent multiple queued operations when using an IO completion system like IOCP or io-uring" I then realized that you couldn't submit moree than one op without risking garbage, since every write could be a "short" one. That might work better if the return value is <code>Result&lt;(), Error&gt;</code></p>



<a name="247424130"></a>
<h4><a href="https://rust-lang.zulipchat.com#narrow/stream/187312-wg-async-foundations/topic/Question%20about%20async%20Read/Write/near/247424130" class="zl"><img src="https://rust-lang.github.io/zulip_archive/assets/img/zulip.svg" alt="view this post on Zulip" style="width:20px;height:20px;"></a> Matthias247 <a href="https://rust-lang.github.io/zulip_archive/stream/187312-wg-async-foundations/topic/Question.20about.20async.20Read.2FWrite.html#247424130">(Jul 28 2021 at 05:44)</a>:</h4>
<blockquote>
<p>This is what happens in async-std when you try to write to a &amp;TcpStream concurrently.</p>
</blockquote>
<p>Ok, that sounds rather bad. With the <code>ManualResetEvent</code> approach you can store multiple wakers, and just wake everyone when the object gets ready. Might still be a storm, but not a constant busy-loop. But yes, you can't have that with a <code>poll</code> function - it needs to return a <code>Future</code> per waiter and you can poll that. But we don't want poll functions anyway ;)</p>



<a name="247424206"></a>
<h4><a href="https://rust-lang.zulipchat.com#narrow/stream/187312-wg-async-foundations/topic/Question%20about%20async%20Read/Write/near/247424206" class="zl"><img src="https://rust-lang.github.io/zulip_archive/assets/img/zulip.svg" alt="view this post on Zulip" style="width:20px;height:20px;"></a> Kestrer <a href="https://rust-lang.github.io/zulip_archive/stream/187312-wg-async-foundations/topic/Question.20about.20async.20Read.2FWrite.html#247424206">(Jul 28 2021 at 05:46)</a>:</h4>
<p><span class="user-mention silent" data-user-id="256841">Nick Cameron</span> <a href="#narrow/stream/187312-wg-async-foundations/topic/Question.20about.20async.20Read.2FWrite/near/247368173">said</a>:</p>
<blockquote>
<p>Sorry, I don't understand why being an immutable reference is important. It seems orthogonal whether a type is bounded by <code>AsyncReadWrite</code> or <code>AsyncRead + AsyncWrite</code> and whether the reference is <code>mut</code> or not</p>
</blockquote>
<p><code>AsyncReadWrite</code> was just one proposed solution to the problem, it is the asynchronous equivalent of a trait like this:</p>
<div class="codehilite" data-code-language="Rust"><pre><span></span><code><span class="k">pub</span><span class="w"> </span><span class="k">trait</span><span class="w"> </span><span class="n">ReadWrite</span>: <span class="nc">Read</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">Write</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="k">fn</span> <span class="nf">split</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span><span class="w"> </span><span class="bp">self</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="p">(</span><span class="k">impl</span><span class="w"> </span><span class="n">Read</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="o">'</span><span class="nb">_</span><span class="p">,</span><span class="w"> </span><span class="k">impl</span><span class="w"> </span><span class="n">Write</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="o">'</span><span class="nb">_</span><span class="p">);</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>
<p>It's not inherently related to async, it's just a way of getting concurrent reads and writes _without_ implementing the Async I/O traits on references (since that's not currently possible).</p>



<a name="247918148"></a>
<h4><a href="https://rust-lang.zulipchat.com#narrow/stream/187312-wg-async-foundations/topic/Question%20about%20async%20Read/Write/near/247918148" class="zl"><img src="https://rust-lang.github.io/zulip_archive/assets/img/zulip.svg" alt="view this post on Zulip" style="width:20px;height:20px;"></a> Alice Ryhl <a href="https://rust-lang.github.io/zulip_archive/stream/187312-wg-async-foundations/topic/Question.20about.20async.20Read.2FWrite.html#247918148">(Aug 02 2021 at 13:11)</a>:</h4>
<p><span class="user-mention" data-user-id="256841">@Nick Cameron</span> Variant D in the linked page is explicitly about implementing the two traits for immutable references.</p>



<hr><p>Last updated: Aug 07 2021 at 22:04 UTC</p>
</html>